#include <string.h>
#include <opencv2/opencv.hpp> 
#include <string.h>
#include "data.h"

namespace sp{

static short ID = 0;

/**
 * @brief 从原始数据构造
 * 
 */

Data::Data(){
    this->app_id = 0;
    this->flow_id = 0;
    this->request_id = ID++;
    this->type = UNKNOW;
    this->next = nullptr;
}

Data::Data(short app_id, short flow_id, short request_id, uint8_t type){
    this->app_id = app_id;
    this->flow_id = flow_id;
    this->request_id = request_id;
    this->type = type;
    this->next = nullptr;
}

Data::Data(short app_id, short flow_id, short request_id, int channels, int rows, int cols, unsigned char* data){
    this->app_id = app_id;
    this->flow_id = flow_id;
    this->request_id = request_id;
    this->type = MAT;
    this->next = nullptr;
    this->context.mat.channels = channels;
    this->context.mat.rows = rows;
    this->context.mat.cols = cols;
    this->context.mat.data = data;
}

Data::Data(short app_id, short flow_id, short request_id, float x, float y, float width, float height){
    this->app_id = app_id;
    this->flow_id = flow_id;
    this->request_id = request_id;
    this->type = RECT;
    this->next = nullptr;
    this->context.rect.x = x;
    this->context.rect.y = y;
    this->context.rect.width = width;
    this->context.rect.height = height;
}

Data::Data(short app_id, short flow_id, short reuqest_id, int C, int H, int W, float* data){
    this->app_id = app_id;
    this->flow_id = flow_id;
    this->request_id = request_id;
    this->type = TENSOR;
    this->next = nullptr;
    this->context.tensor.C = C;
    this->context.tensor.H = H;
    this->context.tensor.W = W;
    this->context.tensor.data = data;
}

Data::Data(short app_id, short flow_id, short reuqest_id, int C, int H, int W, short stream_id, short block_id){
    this->app_id = app_id;
    this->flow_id = flow_id;
    this->request_id = request_id;
    this->type = GPU_TENSOR;
    this->next = nullptr;
    this->context.gpu_tensor.C = C;
    this->context.gpu_tensor.H = H;
    this->context.gpu_tensor.W = W;
    this->context.gpu_tensor.stream_id = stream_id;
    this->context.gpu_tensor.block_id = block_id;
}

Data::Data(short app_id, short flow_id, short reuqest_id, int B, int C, int H, int W, float* data, Data* tensor_head){
    this->app_id = app_id;
    this->flow_id = flow_id;
    this->request_id = request_id;
    this->type = BATCH_TENSOR;
    this->next = nullptr;
    this->context.batch_tensor.B = B;
    this->context.batch_tensor.C = C;
    this->context.batch_tensor.H = H;
    this->context.batch_tensor.W = W;
    this->context.batch_tensor.data = data;
    this->context.batch_tensor.tensor_head = tensor_head;
}

Data::Data(short app_id, short flow_id, short reuqest_id, int B, int C, int H, int W, short stream_id, short block_id, Data* tensor_head){
    this->app_id = app_id;
    this->flow_id = flow_id;
    this->request_id = request_id;
    this->type = BATCH_GPU_TENSOR;
    this->next = nullptr;
    this->context.batch_gpu_tensor.B = B;
    this->context.batch_gpu_tensor.C = C;
    this->context.batch_gpu_tensor.H = H;
    this->context.batch_gpu_tensor.W = W;
    this->context.batch_gpu_tensor.stream_id = stream_id;
    this->context.batch_gpu_tensor.block_id = block_id;
    this->context.batch_gpu_tensor.tensor_head = tensor_head;
}

Data::Data(short app_id, short flow_id, short request_id, const char* str){
    this->app_id = app_id;
    this->flow_id = flow_id;
    this->request_id = request_id;
    this->type = STRING;
    this->next = nullptr;
    assert(strlen(str) < 20);
    strcpy(this->context.string.str, str);
}

Data::Data(Data* da_in){ // 浅复制
    memcpy((void*)this, (void*)da_in, sizeof(Data));
}

/**
 * @brief 从另一个Data构造，主要复制原Data的id
 * 
 */

Data::Data(Data* da_in, uint8_t type){
    this->app_id = da_in->getAppId();
    this->flow_id = da_in->getFlowId();
    this->request_id = da_in->getRequestId();
    this->type = type;
    this->next = nullptr;
}

// MAT
Data::Data(Data* da_in, int channels, int rows, int cols, unsigned char* data){
    this->app_id = da_in->getAppId();
    this->flow_id = da_in->getFlowId();
    this->request_id = da_in->getRequestId();
    this->type = MAT;
    this->next = nullptr;
    this->context.mat.channels = channels;
    this->context.mat.rows = rows;
    this->context.mat.cols = cols;
    this->context.mat.data = data;
}

// RECT
Data::Data(Data* da_in, float x, float y, float width, float height){
    this->app_id = da_in->getAppId();
    this->flow_id = da_in->getFlowId();
    this->request_id = da_in->getRequestId();
    this->type = RECT;
    this->next = nullptr;
    this->context.rect.x = x;
    this->context.rect.y = y;
    this->context.rect.width = width;
    this->context.rect.height = height;
}

// TENSOR
Data::Data(Data* da_in, int C, int H, int W, float* data){
    this->app_id = da_in->getAppId();
    this->flow_id = da_in->getFlowId();
    this->request_id = da_in->getRequestId();
    this->type = TENSOR;
    this->next = nullptr;
    this->context.tensor.C = C;
    this->context.tensor.H = H;
    this->context.tensor.W = W;
    this->context.tensor.data = data;
}

// GPU_TENSOR
Data::Data(Data* da_in, int C, int H, int W, short stream_id, short block_id){
    this->app_id = da_in->getAppId();
    this->flow_id = da_in->getFlowId();
    this->request_id = da_in->getRequestId();
    this->type = GPU_TENSOR;
    this->next = nullptr;
    this->context.gpu_tensor.C = C;
    this->context.gpu_tensor.H = H;
    this->context.gpu_tensor.W = W;
    this->context.gpu_tensor.stream_id = stream_id;
    this->context.gpu_tensor.block_id = block_id;
}

// BATCH_TENSOR
Data::Data(Data* da_in, int B, int C, int H, int W, float* data, std::vector<char*> ptr_vec){
    assert(da_in->type == BATCH_TENSOR || da_in->type == BATCH_GPU_TENSOR);
    assert(da_in->getTensorLength() + 1 == ptr_vec.size());
    // DONE: 完善函数
    // 初始化基本参数
    this->app_id = da_in->getAppId();
    this->flow_id = da_in->getFlowId();
    this->request_id = da_in->getRequestId();
    this->type = BATCH_TENSOR;
    this->next = nullptr;
    this->context.batch_tensor.B = B;
    this->context.batch_tensor.C = C;
    this->context.batch_tensor.H = H;
    this->context.batch_tensor.W = W;
    this->context.batch_tensor.data = data;
    // 初始化tensor_head及后续tensor并链起来
    Data* head = new (ptr_vec[0]) Data(da_in->getTensorHead(), HEAD);
    Data* p = head;
    for(int i = 0; i < da_in->getTensorLength(); i++){
        Data* q = new (ptr_vec[i+1]) Data(da_in->getTensorItem(i), 0, 0, 0, (float*)nullptr);
        p->next = q;
        p = q;
    }
    this->context.batch_tensor.tensor_head = head;
}

// BATCH_GPU_TENSOR
Data::Data(Data* da_in, int B, int C, int H, int W, short stream_id, short block_id, std::vector<char*> ptr_vec){
    assert(da_in->type == BATCH_TENSOR || da_in->type == BATCH_GPU_TENSOR);
    assert(da_in->getTensorLength() + 1 == ptr_vec.size());
    // DONE: 完善函数
    // 初始化基本参数
    this->app_id = da_in->getAppId();
    this->flow_id = da_in->getFlowId();
    this->request_id = da_in->getRequestId();
    this->type = BATCH_GPU_TENSOR;
    this->next = nullptr;
    this->context.batch_gpu_tensor.B = B;
    this->context.batch_gpu_tensor.C = C;
    this->context.batch_gpu_tensor.H = H;
    this->context.batch_gpu_tensor.W = W;
    this->context.batch_gpu_tensor.stream_id = stream_id;
    this->context.batch_gpu_tensor.block_id = block_id;
    // 初始化tensor_head及后续tensor并链起来
    Data* head = new (ptr_vec[0]) Data(da_in->getTensorHead(), HEAD);
    Data* p = head;
    for(int i = 0; i < da_in->getTensorLength(); i++){
        Data* q = new (ptr_vec[i+1]) Data(da_in->getTensorItem(i), 0, 0, 0, (short)0, (short)0);
        p->next = q;
        p = q;
    }
    this->context.batch_gpu_tensor.tensor_head = head;
}

// vec -> BATCH_TENSOR
Data::Data(std::vector<Data*> da_ins, int B, int C, int H, int W, float* data, std::vector<char*> ptr_vec){
    // DONE: 完善函数
    assert(da_ins.size() >= 1);
    assert(da_ins.size() + 1 == ptr_vec.size());
    // 初始化基本参数，id为第一个Data*的id
    this->app_id = da_ins[0]->getAppId();
    this->flow_id = da_ins[0]->getFlowId();
    this->request_id = da_ins[0]->getRequestId();
    this->type = BATCH_TENSOR;
    this->next = nullptr;
    this->context.batch_tensor.B = B;
    this->context.batch_tensor.C = C;
    this->context.batch_tensor.H = H;
    this->context.batch_tensor.W = W;
    this->context.batch_tensor.data = data;
    // 初始化tensor_head及后续tensor并链接链入
    Data* head = new (ptr_vec[0]) Data(da_ins[0], HEAD);
    Data* p = head;
    for(int i = 0; i < da_ins.size(); i++){
        assert(da_ins[i]->type == TENSOR || da_ins[i]->type == GPU_TENSOR);
        Data* q = new (ptr_vec[i+1]) Data(da_ins[i], 0, 0, 0, (float*)nullptr);
        p->next = q;
        p = q;
    }
    this->context.batch_tensor.tensor_head = head;
}

// vec -> BATCH_GPU_TENSOR
Data::Data(std::vector<Data*> da_ins, int B, int C, int H, int W, short stream_id, short block_id, std::vector<char*> ptr_vec){
    // DONE: 完善函数
    assert(da_ins.size() >= 1);
    assert(da_ins.size() + 1 == ptr_vec.size());
    this->app_id = da_ins[0]->getAppId();
    this->flow_id = da_ins[0]->getFlowId();
    this->request_id = da_ins[0]->getRequestId();
    this->type = BATCH_GPU_TENSOR;
    this->next = nullptr;
    this->context.batch_gpu_tensor.B = B;
    this->context.batch_gpu_tensor.C = C;
    this->context.batch_gpu_tensor.H = H;
    this->context.batch_gpu_tensor.W = W;
    this->context.batch_gpu_tensor.stream_id = stream_id;
    this->context.batch_gpu_tensor.block_id = block_id;
    // 初始化tensor_head及后续tensor并链接链入
    Data* head = new (ptr_vec[0]) Data(da_ins[0], HEAD);
    Data* p = head;
    for(int i = 0; i < da_ins.size(); i++){
        assert(da_ins[i]->type == TENSOR || da_ins[i]->type == GPU_TENSOR);
        Data* q = new (ptr_vec[i+1]) Data(da_ins[i], 0, 0, 0, (short)0, (short)0);
        p->next = q;
        p = q;
    }
    this->context.batch_gpu_tensor.tensor_head = head;
}

Data::Data(Data* da_in, const char* str){
    this->app_id = da_in->getAppId();
    this->flow_id = da_in->getFlowId();
    this->request_id = da_in->getRequestId();
    this->type = STRING;
    this->next = nullptr;
    assert(strlen(str) < 20);
    strcpy(this->context.string.str, str);
}

Data::~Data(){

}

void Data::set(int channels, int rows, int cols, unsigned char* data){
    this->type = MAT;
    this->context.mat.channels = channels;
    this->context.mat.rows = rows;
    this->context.mat.cols = cols;
    this->context.mat.data = data;
}

void Data::set(float x, float y, float width, float height){
    this->type = RECT;
    this->context.rect.x = x;
    this->context.rect.y = y;
    this->context.rect.width = width;
    this->context.rect.height = height;
}

void Data::set(int C, int H, int W, float* data){
    this->type = TENSOR;
    this->context.tensor.C = C;
    this->context.tensor.H = H;
    this->context.tensor.W = W;
    this->context.tensor.data = data;
}

void Data::set(int C, int H, int W, short stream_id, short block_id){
    this->type = GPU_TENSOR;
    this->context.gpu_tensor.C = C;
    this->context.gpu_tensor.H = H;
    this->context.gpu_tensor.W = W;
    this->context.gpu_tensor.stream_id = stream_id;
    this->context.gpu_tensor.block_id = block_id;
}

void Data::set(int B, int C, int H, int W, float* data, Data* tensor_head){
    this->type = BATCH_TENSOR;
    this->context.batch_tensor.B = B;
    this->context.batch_tensor.C = C;
    this->context.batch_tensor.H = H;
    this->context.batch_tensor.W = W;
    this->context.batch_tensor.data = data;
    this->context.batch_tensor.tensor_head = tensor_head;
}

void Data::set(int B, int C, int H, int W, short stream_id, short block_id, Data* tensor_head){
    this->type = BATCH_GPU_TENSOR;
    this->context.batch_gpu_tensor.B = B;
    this->context.batch_gpu_tensor.C = C;
    this->context.batch_gpu_tensor.H = H;
    this->context.batch_gpu_tensor.W = W;
    this->context.batch_gpu_tensor.stream_id = stream_id;
    this->context.batch_gpu_tensor.block_id = block_id;
    this->context.batch_gpu_tensor.tensor_head = tensor_head;
}

void Data::set(const char* str){
    this->type = STRING;
    assert(strlen(str) < 20);
    strcpy(this->context.string.str, str);
}

short Data::getAppId(){
    return this->app_id;
}

short Data::getFlowId(){
    return this->flow_id;
}

short Data::getRequestId(){
    return this->request_id;
}

std::string Data::getStr(){
    assert(this->type == STRING);
    std::string str(this->context.string.str);
    return str;
}

void Data::setRefCount(int ref_count){
    this->ref_count = ref_count;
}

int Data::subRefCount(){
    this->ref_count--;
    return this->ref_count;
}

int Data::getRefCount(){
    return ref_count;
}

int Data::getLength(){
    if(type == HEAD){
        int res = 0;
        Data* d = next;
        while(d){
            res++;
            d = d->next;
        }
        return res;
    }else
        return -1;
}

Data* Data::getItem(int i){
    assert(type == HEAD);
    Data* d = next;
    while(i){
        i--;
        assert(d != nullptr);
        d = d->next;
    }
    return d;
}

int Data::getTensorLength(){
    assert(type == BATCH_TENSOR || type == BATCH_GPU_TENSOR);
    if(type == BATCH_TENSOR)
        return this->context.batch_tensor.tensor_head->getLength();
    else
        return this->context.batch_gpu_tensor.tensor_head->getLength();
}

Data* Data::getTensorHead(){
    assert(type == BATCH_TENSOR || type == BATCH_GPU_TENSOR);
    if(type == BATCH_TENSOR)
        return this->context.batch_tensor.tensor_head;
    else
        return this->context.batch_gpu_tensor.tensor_head;
}

Data* Data::getTensorItem(int i){
    assert(type == BATCH_TENSOR || type == BATCH_GPU_TENSOR);
    Data* p;
    if(type == BATCH_TENSOR){
        assert(this->context.batch_tensor.tensor_head != nullptr);
        p = this->context.batch_tensor.tensor_head->next;
    }else{
        assert(this->context.batch_gpu_tensor.tensor_head != nullptr);
        p = this->context.batch_gpu_tensor.tensor_head->next;
    }
    while(i){
        i--;
        assert(p != nullptr);
        p = p->next;
    }
    return p;
}

};